1 module hip.wasm;
2 version(WebAssembly):
3 
4 ///WebAssembly.Table replacement for HipremeEngine
5 private __gshared ubyte* function(ubyte* args)[] _annonymousFunctionTable;
6 ///JSFunctions are represented opaquely right now.
7 alias JSFunction(T) = ubyte*;
8 
9 ///Gets a unique function index for usage in the table
10 extern(C) size_t _getFuncAddress(ubyte* fn);
11 
12 ///Javascript function to call a D callback.
13 export extern(C) ubyte* __callDFunction(size_t addr, ubyte* args)
14 {
15 	return _annonymousFunctionTable[addr](args);
16 }
17 
18 ///Checks if function has been called with required arguments.
19 private ubyte* validateArguments(alias fn)(ubyte* args)
20 {
21 	import std.traits;
22 	//Only checking the count of 
23 	assert(Parameters!(fn).length <= *cast(size_t*)args, 
24 	fn.stringof~"Expected "~Parameters!(fn).length.stringof~" parameters");
25 	return args + size_t.sizeof; //Only uses 1 size_t to determine arguments validity
26 }
27 
28 
29 struct Arguments(alias Func)
30 {
31 	import std.traits;
32 	Parameters!Func params;
33 }
34 
35 Struct loadMemoryInStruct(Struct)(ubyte* arg)
36 {
37 	Struct ret;
38 	size_t last = 0;
39 	foreach(ref v; ret.tupleof)
40 	{
41 		static if(is(typeof(v) : string))
42 		{
43 			{
44 				ubyte* data = arg+last;
45 				size_t length = *cast(size_t*)data;
46 				v = cast(string)data[size_t.sizeof..length+size_t.sizeof];
47 			}
48 		}
49 		else static if(is(typeof(v) : ubyte[]))
50 		{
51 			{
52 				ubyte* data = arg+last;
53 				size_t length = *cast(size_t*)data;
54 				v = cast(ubyte[])data[size_t.sizeof..length+size_t.sizeof];
55 			}
56 		}
57 		else
58 			memcpy(&v, arg+last, v.sizeof);
59 		last+= v.sizeof;
60 	}
61 	return ret;
62 }
63 
64 Arguments!fn wasmParametersFromUbyte(alias fn)(ubyte* arg)
65 {
66 	return loadMemoryInStruct!(Arguments!fn)(arg);
67 }
68 
69 /**
70 *	Whenever wanting to pass a callback to Javascript, call this function instead.
71 *	This function is not expected to meet usercode. But it will stay here nevertheless. 
72 */
73 ubyte* sendJSFunction(alias fn)()
74 {
75 	import std.traits;
76 	static ubyte* function(ubyte* arg) convertedFunc  = (ubyte* arg)
77 	{
78 		Arguments!fn params = wasmParametersFromUbyte!fn(validateArguments!fn(arg));
79 		static if(!is(ReturnType!fn == void))
80 			return fn(params.tupleof);
81 		else
82 		{
83 			fn(params.tupleof);
84 			return null;
85 		}
86 	};
87 	size_t addr = _getFuncAddress(cast(ubyte*)fn);
88 	if(addr >= _annonymousFunctionTable.length) _annonymousFunctionTable.length = addr+1;
89 	_annonymousFunctionTable[addr] = convertedFunc;
90 
91 	return cast(ubyte*)fn;
92 }
93 
94 
95 struct JSDelegate
96 {
97 	ubyte* funcHandle;
98 	ubyte* funcptr;
99 	ubyte* ctx;
100 }
101 
102 alias JSStringType = AliasSeq!(size_t, void*);
103 
104 struct JSString
105 {
106 	size_t length;
107 	void* ptr;
108 	this(string str)
109 	{
110 		length = str.length;
111 		ptr = cast(void*)str.ptr;
112 	}
113 }
114 
115 alias JSDelegateType(T) = AliasSeq!(ubyte*, ubyte*, ubyte*);
116 
117 JSDelegate sendJSDelegate(alias dg)()
118 {
119 	import std.traits;
120 	auto convertedFunc = toFunc!dg;
121 	size_t addr = _getFuncAddress(cast(ubyte*)convertedFunc);
122 	if(addr >= _annonymousFunctionTable.length) _annonymousFunctionTable.length = addr+1;
123 	_annonymousFunctionTable[addr] = convertedFunc;
124 
125 
126 	return JSDelegate(cast(ubyte*)addr, cast(ubyte*)dg.funcptr, cast(ubyte*)dg.ptr);
127 }
128 
129 
130 
131 /**
132 * Generates a delegate which adds the `this` context from the arguments.
133 * It also packs the arguments sent from Javascript and transform them to the
134 * data the D delegate expects. There is also a validation in the arguments received.
135 */
136 ubyte* function(ubyte* args) toFunc(alias dg)()
137 {
138 	import std.traits;
139 	import hip.wasm;
140 	alias Params = Parameters!dg;
141 	alias DgArgs = Arguments!dg;
142 	enum Length = DgArgs.tupleof.length;
143 	alias Ret = ReturnType!dg;
144 
145 	static ubyte* function(ubyte* arg) ret = (ubyte* arg)
146 	{
147 		size_t argsCount = *cast(size_t*)arg;
148 		assert(argsCount >= 2, "D delegates expects at least 2 arguments [Function Pointer, Function Context]");
149 		assert(argsCount - 2 <= Length, "Expected "~Length.stringof~" parameters.");
150 		size_t[3] baseArgs = (cast(size_t*)arg)[0..3];
151 
152 		static DgArgs delegateArguments;
153 		static if(Length > 0)
154 			delegateArguments = loadMemoryInStruct!DgArgs(arg + size_t.sizeof*3);
155 		
156 		static if(Length > 0) 
157 		{
158 			static if(is(Ret == void))
159 				void delegate(Params) dg;
160 			else
161 				ubyte* delegate(Params) dg;
162 		}
163 		else
164 		{
165 			static if(is(Ret == void))
166 				void delegate() dg;
167 			else
168 				ubyte* delegate() dg;
169 		}
170 		
171 		dg.funcptr = cast(typeof(dg.funcptr))baseArgs[1];
172 		dg.ptr = cast(void*)baseArgs[2];
173 
174 		static if(Length > 0)
175 		{
176 			static if(is(Ret == void))
177 			{
178 				dg(delegateArguments.tupleof);
179 				return null;
180 			} else return dg(delegateArguments.tupleof);
181 		}
182 		else
183 		{
184 			static if(is(Ret == void))
185 			{
186 				dg();
187 				return null;
188 			} else return dg();
189 		}
190 	};
191 	return ret;
192 }
193 
194 
195 
196 ubyte[] getWasmBinary(ubyte* input)
197 {
198 	size_t length = *cast(size_t*)input;
199 	ubyte[] ret = (input+size_t.sizeof)[0..length];
200 	return ret;
201 }
202 
203 void freeWasmBinary(ubyte[] binary)
204 {
205 	import core.memory;
206 	ubyte* ptr = binary.ptr - size_t.sizeof;
207 	GC.free(ptr);
208 }